
open Ast
open Ast_fullfill
open Ast_effectable
open Ast_memory

(* TODO
	Gerer les bool exact pour objets
	Gerer la memoire pour les ifs
*)


exception Evaluation_error

let evaluate ast =

	let unaryExpression_from_jsValue value = 
		AUnaryExpr(APostfixExpr(
			ALeftHandSide(
				ANewExpression(
					AMemberExpression(
						APrimaryExpr(
			match value with
				| VUndefined -> AIdentifier("undefined")
				| _ -> ALiteral(match value with
					| VNull -> ANull
					| VBool b -> ABooleanLiteral b
					| VString s -> ALitStringLiteral s
					| VNumber n -> ALitNumericLiteral (string_of_flint n)
					| _ -> ANull
					)
		))))))
		in

	let labelize_stmts labels stmts = 
		let stmt = ABlock(stmts) in
		let fullfill = fullfill_statement {
			idents = StrSet.empty;
			labels = labels;
			locals = StrSet.empty
			} stmt in
		let map,_,_ = Tools_identifier.free_ident (StrMap.empty,fullfill.labels, StrSet.empty) "?" false in
		let ident = StrMap.find "?" map in
		let stmt = Evaluator_labelize.labelize_statement ident stmt in
		ALabelledStatement (ident, stmt) in

	let rec evaluate_list func variables isCertain context = function
		| [] -> defaultReturn, [], variables, []
		| a::b -> let value, sideEffects, variables, element = func variables isCertain context a in
			if value.returnsAlways || value.throws || value.anonymousBreak || (value.breaks <> []) then
				value, sideEffects, variables, [element]
			else
				let value1, sideEffects1, variables, sublist = evaluate_list func variables isCertain context b in
				{
					returns = value.returns@value1.returns;
					returnsAlways = value1.returnsAlways;
					throws = value1.throws;
					breaks = value1.breaks;
					anonymousBreak = value1.anonymousBreak
				}, sideEffects@sideEffects1, variables, element::sublist in

		
	let rec get_value variables isCertain context expr =
		memory_get_value evaluate_realFunctionContent variables isCertain context expr

	and set_value variables isCertain context value expr =
		memory_set_value evaluate_realFunctionContent variables isCertain context value expr

	and erase_sideEffects variables isCertain context = 
		memory_erase_sideEffects evaluate_realFunctionContent variables isCertain context

	and evaluate_program program = 
		
		let variables = create_env () in
		
		let program = Ast_tools.move_funcDecls_to_left program in

		let fullfill = fullfill_list fullfill_sourceElement {
			idents = StrSet.empty;
			labels = StrSet.empty;
			locals = StrSet.empty
			} program in
		let context = {
			co_context = 0;
			co_current = None;
			co_nesting = 0
			} in
			
		let functions = Ast_tools.funcDecls_to_map program in

		StrSet.fold (fun ident _ -> ignore(set_value variables true context VUndefined (VString ident)) ) fullfill.idents ();
		StrMap.fold (fun ident (is_inline,params,body) _ -> 
			let index = create_function is_inline params body true in
			ignore(set_value variables true context (VMemory index) (VString ident))) functions ();
		let _, _, _, ast = evaluate_list (evaluate_sourceElement StrSet.empty) variables true context program in ast

	and evaluate_sourceElement labels variables isCertain context = function
		| AStatement(stmt) -> let value, sideEffects, variables, stmt = evaluate_statement labels variables isCertain context stmt in 
			value, sideEffects, variables, AStatement(stmt)
		(*| AFunctionDeclaration(funcDecl) -> let value, sideEffects, variables, funcDecl = evaluate_functionDeclaration labels variables isCertain context funcDecl in 
		value, sideEffects, variables, AFunctionDeclaration(stmt) *) (* Modif #1 *)

	and evaluate_statement labels variables isCertain context = function
		| ABlock(block) -> let return, sideEffects, variables, block = 
			evaluate_block labels variables isCertain context block in
			return, sideEffects, variables, ABlock (block)
		| AVariableStatement(stmt) -> let return, sideEffects, variables,stmt = 
			evaluate_variableStatement variables isCertain context stmt 
			in return, sideEffects, variables,AVariableStatement(stmt)
		| AConstStatement(stmt) -> let return, sideEffects, variables,stmt = 
			evaluate_constStatement variables isCertain context stmt 
			in return, sideEffects, variables, AConstStatement(stmt)
		| AFuncDeclaration(funcDecl) -> let return, sideEffects, variables, funcDecl = 
			evaluate_functionDeclaration variables isCertain context funcDecl 
			in return, sideEffects, variables,AFuncDeclaration(funcDecl)	(* Modif #1 *)
		| AEmptyStatement -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = [];
			anonymousBreak = false;
			}, [], variables, AEmptyStatement
		| AExpressionStatement(stmt) -> evaluate_expressionStatement variables isCertain context stmt
		| AIfStatement(stmt) -> evaluate_ifStatement labels variables isCertain context stmt
		| AIterationStatement(stmt) -> 
			let return, sideEffects, variables, stmt = evaluate_iterationStatement labels variables isCertain context stmt in
			let return = {
					returns = return.returns;
					returnsAlways = return.returnsAlways;
					throws = return.throws;
					breaks = return.breaks;
					anonymousBreak = false
			} in 
			return, sideEffects, variables, stmt
		| AContinueStatement(stmt) -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = (match stmt with | Some ident -> [ident] | None -> []);
			anonymousBreak = (stmt = None);
			}, [], variables, AContinueStatement(stmt)
		| ABreakStatement(stmt) -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = (match stmt with | Some ident -> [ident] | None -> []);
			anonymousBreak = (stmt = None);
			}, [], variables, ABreakStatement(stmt)
		| AReturnStatement(stmt) -> evaluate_returnStatement variables isCertain context stmt
		| AWithStatement(stmt) -> evaluate_withStatement labels variables isCertain context stmt
		| ALabelledStatement(stmt) -> evaluate_labelledStatement labels variables isCertain context stmt
		| ASwitchStatement(stmt) -> evaluate_switchStatement labels variables isCertain context stmt
		| AThrowStatement(stmt) -> evaluate_throwStatement variables isCertain context stmt
		| ATryStatement(stmt) -> evaluate_tryStatement labels variables isCertain context stmt
		| ADebuggerStatement -> {
			returns = [];
			returnsAlways = false;
			throws = false;
			breaks = [];
			anonymousBreak = false;
			}, [], variables, ADebuggerStatement

	and evaluate_block labels variables isCertain context stmts = 
		let value, sideEffects, variables, stmts = evaluate_list (evaluate_statement labels) variables isCertain context stmts in
			(value, sideEffects, variables,List.filter (fun e -> (match e with
				| AEmptyStatement -> false
				| _ -> true)) stmts)

	and evaluate_variableStatement variables isCertain context (is_private,is_meta,stmt) =
		let value, sideEffects, variables, stmt = evaluate_list (evaluate_variableDeclaration true) variables isCertain context stmt in
		value, sideEffects, variables, (is_private,is_meta,stmt)

	and evaluate_constStatement variables isCertain context (is_private, is_meta,stmt) =
		let value, sideEffects, variables, stmt =  evaluate_list (evaluate_variableDeclaration false) variables isCertain context stmt in
		value, sideEffects, variables, (is_private,is_meta,stmt)

	and evaluate_returnStatement variables isCertain context = function
		| None -> {
				returns = [VUndefined];
				returnsAlways = true;
				throws = false;
				breaks = [];
				anonymousBreak = false;
			}, [], variables, AReturnStatement(None)
		| Some expr -> let exprValue,_,sideEffects,variables,expr = evaluate_expression variables isCertain context expr in
			{
				returns = [exprValue];
				returnsAlways = true;
				throws = false;
				breaks = [];
				anonymousBreak = false;
			}, sideEffects, variables, AReturnStatement(Some expr)

	and evaluate_withStatement labels variables isCertain context (expr,stmt) =
		let _,_,sideEffects1,variables,expr = evaluate_expression variables isCertain context expr in
		let value, sideEffects2, variables, stmt = evaluate_statement labels variables isCertain context stmt in
			value, sideEffects1@sideEffects2, variables, AWithStatement(expr,stmt)

	and evaluate_labelledStatement labels variables isCertain context (ident,stmt) =
		let return, sideEffects, variables, stmt = evaluate_statement (StrSet.add ident labels) variables isCertain context stmt in
		let return = {
				returns = return.returns;
				returnsAlways = return.returnsAlways;
				throws = return.throws;
				breaks = List.filter (fun i -> i <> ident) return.breaks;
				anonymousBreak = return.anonymousBreak;
		} in
		return, sideEffects, variables, ALabelledStatement(ident,stmt)

	and evaluate_throwStatement variables isCertain context expr =
		let _,_,sideEffects, variables, expr = evaluate_expression variables isCertain context expr in
		{
			returns = [];
			returnsAlways = false;
			throws = true;
			breaks = [];
			anonymousBreak = false;
		}, sideEffects, variables, AThrowStatement(expr)

	and evaluate_tryStatement labels variables isCertain context = function
		| ATryCatch(block, catch) -> let return1, sideEffects1, variables, block = evaluate_block labels variables isCertain context block in
			let return2, sideEffects2, variables, catch = evaluate_catch labels variables false context catch in
			let variables = erase_sideEffects variables isCertain context (sideEffects1@sideEffects2) in
			{
				returns = if return1.throws then return2.returns else return1.returns@return2.returns;
				returnsAlways = if return1.throws then return2.returnsAlways else return1.returnsAlways && return2.returnsAlways;
				throws = return1.throws && return2.throws;
				breaks = [];				(* Could be discussed. If there is a catch, there might not append break *)
				anonymousBreak = false;		(* Could be discussed *)
			}, sideEffects1@sideEffects2, variables, ATryStatement(ATryCatch(block,catch))
		| ATryFinally(block, finally) -> let return1, sideEffects1, variables, block = evaluate_block labels variables isCertain context block in
			let return2, sideEffects2, variables, finally = evaluate_finally labels variables isCertain context finally in
			let variables = erase_sideEffects variables isCertain context sideEffects1 in
			{
				returns = if return2.returnsAlways then return2.returns else return1.returns@return2.returns;
				returnsAlways = return1.returnsAlways || return2.returnsAlways;
				throws = return1.throws || return2.throws;
				breaks = if (return2.breaks <> []) || return2.anonymousBreak then return2.breaks  else return1.breaks;
				anonymousBreak = (return1.anonymousBreak && (return2.breaks = [])) || return2.anonymousBreak
			
			}, sideEffects1@sideEffects2, variables, ATryStatement(ATryFinally(block,finally))
		| ATryCatchFinally(block, catch, finally ) -> let return1, sideEffects1, variables, block = evaluate_block labels variables isCertain context block in
			let return2, sideEffects2, variables, catch = evaluate_catch labels variables false context catch in
			let variables = erase_sideEffects variables isCertain context (sideEffects1@sideEffects2) in
			let return3, sideEffects3, variables, finally = evaluate_finally labels variables isCertain context finally in
			{
				returns = if return3.returnsAlways then return3.returns else (if return1.throws then return2.returns else return1.returns@return2.returns)@return3.returns;
				returnsAlways = (if return1.throws then return2.returnsAlways else return1.returnsAlways && return2.returnsAlways) || return3.returnsAlways;
				throws = (return1.throws && return2.throws) || return3.throws;
				breaks = if (return3.breaks <> []) || return3.anonymousBreak then return3.breaks  else return1.breaks@return2.breaks;
				anonymousBreak = (return1.anonymousBreak && return2.anonymousBreak && (return3.breaks = [])) || return3.anonymousBreak
			}, sideEffects1@sideEffects2@sideEffects3, variables, ATryStatement(ATryCatchFinally(block,catch,finally))

	and evaluate_catch labels variables isCertain context (ident,block) =
		let variables = StrMap.add ident (VarVar VUnknown) variables in
		let return, sideEffects, variables, block = evaluate_block labels variables isCertain context block in
			return, sideEffects, variables, (ident,block)

	and evaluate_finally labels variables isCertain context block =
		evaluate_block labels variables isCertain context block

	and evaluate_switchStatement labels variables isCertain context (expr,caseBlock) =
		let exprValue,_,sideEffects1,variables,expr = evaluate_expression variables isCertain context expr in
		let return, sideEffects2, variables, stmt = evaluate_caseBlock labels variables isCertain context expr exprValue caseBlock in
		{
			returns = return.returns;
			returnsAlways = return.returnsAlways;
			throws = return.throws;
			breaks = return.breaks;
			anonymousBreak = false
		}, sideEffects1@sideEffects2, variables, if sideEffects1 = [] then stmt else 
			match stmt with
				| ABlock stmts -> ABlock ((AExpressionStatement(expr))::stmts)
				| AEmptyStatement -> (AExpressionStatement(expr))
				| _ -> stmt
	
	and test_find_caseClauses variables isCertain context switchValue = function
		| [] -> Some false
		| caseClause::caseClauses -> 
			let expr, _ = caseClause in
			let exprValue, _,sideEffects1, variables, expr = evaluate_expression variables isCertain context expr in
			(match switchValue with
				| VUnknownButTrue | VUnknownButFalse | VUnknown -> None
				| _ -> (match exprValue with
					| VUnknownButTrue | VUnknownButFalse | VUnknown -> None
					| _ -> 
						(match merge_types exprValue switchValue with
							| VUnknownButTrue | VUnknownButFalse | VUnknown -> 
								test_find_caseClauses variables isCertain context switchValue caseClauses
							| _ -> (Some true)
						)
					)
			)

	and evaluate_caseBlock labels variables isCertain context switchExpr switchValue = function
		| ACasesNoDefault(caseClauses) -> 
			let find = test_find_caseClauses variables isCertain context switchValue caseClauses in
			(match find with
				| None ->
					let _, return, sideEffects, variables, caseClauses = evaluate_caseClauses labels variables false context switchValue false caseClauses in
						return, sideEffects, variables, ASwitchStatement(switchExpr, ACasesNoDefault(caseClauses))
				| Some false -> defaultReturn, [], variables, AEmptyStatement
				| Some true -> let _, return, sideEffects, variables, stmts = evaluate_caseClausesAsStmt labels variables isCertain context switchValue caseClauses in
				return, sideEffects, variables, (labelize_stmts labels stmts)
			)
			
		| ACasesDefault(caseClauses1, defaultClause, caseClauses2) -> 
			let find = test_find_caseClauses variables isCertain context switchValue caseClauses1 in
			let find = (match find with
				| None -> None
				| Some true -> Some true
				| Some false -> test_find_caseClauses variables isCertain context switchValue caseClauses2
				) in
			(match find with
				| None -> let found, return1, sideEffects1, variables, caseClauses1 = evaluate_caseClauses labels variables false context switchValue false caseClauses1 in
					let return2, sideEffects2, variables, defaultClause = evaluate_defaultClause labels variables false context defaultClause in
					let variables = erase_sideEffects variables isCertain context sideEffects2 in
					let found, return3, sideEffects3, variables, caseClauses2 = evaluate_caseClauses labels variables false context switchValue true caseClauses2 in
					let fullSideEffects = sideEffects1@sideEffects2@sideEffects3 in
					(*let variables = if found then variables else erase_sideEffects variables isCertain context fullSideEffects in*)
					{
						returns = return1.returns@return2.returns@return3.returns;
						returnsAlways = return1.returnsAlways && return2.returnsAlways && return3.returnsAlways;
						throws = return1.throws && return2.throws && return3.throws;
						breaks = list_intersect (return1.breaks::return2.breaks::return3.breaks::[]);
						anonymousBreak = false
					}, fullSideEffects, variables, ASwitchStatement(switchExpr, ACasesDefault(caseClauses1, defaultClause, caseClauses2))
				| Some false ->
					let _, _, sideEffects, variables, stmts1 = evaluate_caseClausesAsStmt labels variables isCertain context switchValue caseClauses1 in 
					let stmts = defaultClause @ (List.fold_left (fun lst (_,stmts) -> lst@stmts) [] caseClauses2) in 
					let return1, sideEffects1, variables, stmts = evaluate_list (evaluate_statement labels) variables isCertain context stmts in
					return1, sideEffects1, variables, (labelize_stmts labels (stmts1@stmts))
				| Some true -> 
					let found, _, sideEffects1, variables, stmts1 = evaluate_caseClausesAsStmt labels variables isCertain context switchValue caseClauses1 in 
					if found then
						let stmts2 = defaultClause @ (List.fold_left (fun lst (_,stmts) -> lst@stmts) [] caseClauses2) in 
						let return2, sideEffects2, variables, stmts2 = evaluate_list (evaluate_statement labels) variables isCertain context stmts2 in
						return2, (sideEffects1@sideEffects2), variables, (labelize_stmts labels (stmts1@stmts2))
					else
						let _, return2, sideEffects2, variables, stmts2 = evaluate_caseClausesAsStmt labels variables isCertain context switchValue caseClauses2 in 
						return2, (sideEffects1@sideEffects2), variables, (labelize_stmts labels (stmts1@stmts2))
					
			)

	and evaluate_caseClausesAsStmt labels variables isCertain context switchValue = function
		| [] -> false, defaultReturn, [], variables, []
		| caseClause::caseClauses -> let found = test_find_caseClauses variables isCertain context switchValue [caseClause] in
			let expr, _ = caseClause in
			let _, _,sideEffects1, variables, expr = evaluate_expression variables isCertain context expr in
			let found, return2, sideEffects2, variables, stmts = (match found with
				| None -> false, defaultReturn, [], variables, [] (* Should never happen *)
				| Some false -> evaluate_caseClausesAsStmt labels variables isCertain context switchValue caseClauses
				| Some true -> let stmts = List.fold_left (fun lst (_,stmts) -> lst@stmts) [] (caseClause::caseClauses) in
					let return2, sideEffects2, variables, stmts = evaluate_list (evaluate_statement labels) variables isCertain context stmts in
					true, return2, sideEffects2, variables, stmts) in
			found, return2, sideEffects1@sideEffects2, variables, (if sideEffects1 = [] then stmts else (AExpressionStatement(expr))::stmts)

	and evaluate_caseClauses labels variables isCertain context switchValue found = function
		| [] -> found, defaultReturn, [], variables, []
		| caseClause::caseClauses -> let foundInClause, return1, sideEffects1, variables, caseClause = evaluate_caseClause labels variables isCertain context switchValue found caseClause in
			let found, return2, sideEffects2, variables, caseClauses = evaluate_caseClauses labels variables isCertain context switchValue foundInClause caseClauses in
			let variables = if foundInClause then erase_sideEffects variables isCertain context sideEffects1 else variables in
			found, {
				returns = return1.returns@return2.returns;
				returnsAlways = return1.returnsAlways && return2.returnsAlways;
				throws = return1.throws && return2.throws;
				breaks = list_intersect (return1.breaks::return2.breaks::[]);
				anonymousBreak = return1.anonymousBreak && return2.anonymousBreak
			}, sideEffects1@sideEffects2, variables, if foundInClause 
				then 
				caseClause::caseClauses 
				else if sideEffects1 = [] then caseClauses
				else let expr, _ = caseClause in (expr,[])::caseClauses

	and evaluate_caseClause labels variables isCertain context switchValue found (expr,stmts) =
		let exprValue, _,sideEffects1, variables, expr = evaluate_expression variables isCertain context expr in
		if found then
			let return2, sideEffects2, variables, stmts = evaluate_list (evaluate_statement labels) variables isCertain context stmts in
				found, return2, sideEffects1@sideEffects2, variables, (expr,stmts)
		else
			let found = test_find_caseClauses variables isCertain context switchValue [(expr,stmts)] in
			(match found with
				| None -> let return2, sideEffects2, variables, stmts = evaluate_list (evaluate_statement labels) variables isCertain context stmts in
					true, return2, sideEffects1@sideEffects2, variables, (expr,stmts)
				| Some _ -> false, defaultReturn, sideEffects1, variables, (expr,stmts)
			)

	and evaluate_defaultClause labels variables isCertain context stmts =
		evaluate_list (evaluate_statement labels) variables isCertain context stmts

	and evaluate_variableDeclaration isvar variables isCertain context (ident,init) =
		match init with
			| None -> (*let variables = StrMap.add ident (if isvar then (VarVar VUndefined) else (VarConst VUndefined)) variables in*)
				defaultReturn, [], variables,(ident,None)
			| Some init -> let v, sideEffects, variables, init = evaluate_initializer variables isCertain context init in
				let variables = if context.co_nesting = 0 then (ignore(set_value variables isCertain context v (VString ident)); variables)
				else StrMap.add ident (if isvar then (VarVar v) else (VarConst v)) variables in
				defaultReturn, sideEffects, variables,(ident,Some init)

	and evaluate_initializer variables isCertain context assignExpr =
		let exprValue, _, sideEffects, variables, assignExpr = evaluate_assignmentExpression variables isCertain context assignExpr in
		exprValue, sideEffects, variables, assignExpr

	and evaluate_functionDeclaration variables isCertain context (is_private,is_meta,is_inline,ident,params,body) =
		let index = create_function is_inline params body true in
		let variables = StrMap.add ident (VarVar(VMemory index)) variables in
		let _, _, body = evaluate_functionContent variables isCertain {
				co_current = None;
				co_context = context.co_context;
				co_nesting = context.co_nesting
			} params body in
			defaultReturn, [], variables,(is_private,is_meta,is_inline,ident,params,body)

	and evaluate_functionExpression variables isCertain context (is_inline,ident,params,body) =
		let index = create_function is_inline params body true in
		let _, _, body = (match ident with
			| None ->
				evaluate_functionContent variables isCertain {
					co_current = None;
					co_context = context.co_context;
					co_nesting = context.co_nesting
				} params body
			| Some ident ->
				let variables = StrMap.add ident (VarVar (VMemory index)) variables in
				evaluate_functionContent variables isCertain {
					co_current = None;
					co_context = context.co_context;
					co_nesting = context.co_nesting
				} params body)
			in (VMemory(index)),defaultRefValue,[],variables,(is_inline,ident,params,body)

	and evaluate_functionContent variables isCertain context params body =
		let memory = memory_reinstall () in
		let variables = StrMap.fold (fun key value map -> match value with
			| VarConst _ -> StrMap.add key value map
			| VarVar _ -> StrMap.add key (VarVar VUnknown) map) variables StrMap.empty in
		let variables = List.fold_left (fun variables ident -> StrMap.add ident (VarVar VUnknown) variables) variables params in
		let exprValue,_,body = evaluate_functionBody variables context body in
		let _ = memory_reset memory in
		exprValue, [], body

	and evaluate_realFunctionContent variables isCertain context params body values =
		if context.co_nesting > 100 then VUnknown, [(defaultRefValue,VUnknown)] else
		let values = if (List.length values) < (List.length params) then
			list_fill_right ((List.length params) - (List.length values)) VUndefined values
			else (list_sub (List.length params) values) in
		let variables = List.fold_left2 (fun variables param value -> StrMap.add param (VarVar value) variables) variables params values in
		let value, sideEffects, _ = evaluate_functionBody variables context body in
		value, sideEffects

	and evaluate_functionBody variables context body =
	
		let body = Ast_tools.move_funcDecls_to_left body in
		
		let fullfill = fullfill_list fullfill_sourceElement {
			idents = StrSet.empty;
			labels = StrSet.empty;
			locals = StrSet.empty
			} body in
			
		let effectable = effectable_list effectable_sourceElement StrSet.empty StrSet.empty body in

		let old_effectable = effectable_set effectable in
		
		let context = {
			co_context = (match context.co_current with | None -> 0 | Some e -> e);
			co_current = None;
			co_nesting = context.co_nesting+1
		} in
		
		let functions = Ast_tools.funcDecls_to_map body in
		let variables = StrSet.fold (fun e variables -> StrMap.add e (VarVar VUndefined) variables) fullfill.idents variables in
		let variables = StrMap.fold (fun ident (is_inline,params,body) -> 
			let index = create_function is_inline params body true in
			StrMap.add ident (VarVar (VMemory index))) functions variables in
		let return, sideEffects, _, body = evaluate_list (evaluate_sourceElement StrSet.empty) variables false context body in 
		let exprValue = if return.returnsAlways then merge_types_list return.returns else merge_types_list (VUndefined::return.returns) in
		
		let _ = effectable_set old_effectable in
		
		exprValue,sideEffects,body

	and evaluate_expressionStatement variables isCertain context stmt =
		let _, _, sideEffects, variables,stmt = evaluate_expression variables isCertain context stmt in
		defaultReturn, sideEffects, variables, (if sideEffects <> [] then (AExpressionStatement(stmt)) else AEmptyStatement)

	and evaluate_ifStatement labels variables isCertain context = function
		| AIfElse(expr,stmt1,stmt2) ->
			let exprValue, _, sideEffects, variables, expr = evaluate_expression variables isCertain context expr in
			if sideEffects <> [] then
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
					let return2, sideEffects2, variables, stmt = evaluate_statement labels variables isCertain context stmt2 in
						return2, sideEffects@sideEffects2, variables,(AIfStatement(
							AIf( [ACondExpr(
									ACondBinaryExpr(
										AUnaryExpr(
											AOpUnaryExpr("!",
												APostfixExpr(
													ALeftHandSide(
														ANewExpression(
															AMemberExpression(
																APrimaryExpr(
																	AExpression(expr))))))))))],stmt)))
					| VBool true | VString _ | VNumber _ | VMemory _ | VUnknownButTrue -> 
					let return1, sideEffects1, variables, stmt = evaluate_statement labels variables isCertain context stmt1 in
						return1, sideEffects@sideEffects1, variables,(AIfStatement(AIf(expr,stmt)))
					| _ -> let return1, sideEffects1, variables, stmt1 = evaluate_statement labels variables false context stmt1 in
						let return2, sideEffects2, variables, stmt2 = evaluate_statement labels variables false context stmt2 in
						let sideEffects2 = sideEffects1@sideEffects2 in
						let variables = erase_sideEffects variables isCertain context sideEffects2 in
							{
								returns = return1.returns@return2.returns;
								returnsAlways = return1.returnsAlways && return2.returnsAlways;
								throws = return1.throws && return2.throws;
								breaks = list_intersect (return1.breaks::return2.breaks::[]);
								anonymousBreak = return1.anonymousBreak && return2.anonymousBreak
							}, sideEffects@sideEffects2, variables, (AIfStatement(AIfElse(expr, stmt1, stmt2))))
			else
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse-> 
						evaluate_statement labels variables isCertain context stmt2
					| VBool true | VString _ | VNumber _ | VMemory _ | VUnknownButTrue -> 
						evaluate_statement labels variables isCertain context stmt1
					| _ -> let return1, sideEffects1, variables, stmt1 = evaluate_statement labels variables false context stmt1 in
						let return2, sideEffects2, variables, stmt2 = evaluate_statement labels variables false context stmt2 in
						let sideEffects2 = sideEffects1@sideEffects2 in
						let variables = erase_sideEffects variables isCertain context sideEffects2 in
							{
								returns = return1.returns@return2.returns;
								returnsAlways = return1.returnsAlways && return2.returnsAlways;
								throws = return1.throws && return2.throws;
								breaks = list_intersect (return1.breaks::return2.breaks::[]);
								anonymousBreak = return1.anonymousBreak && return2.anonymousBreak
							}, sideEffects2, variables, (AIfStatement(AIfElse(expr, stmt1, stmt2))))
		| AIf(expr,stmt) ->
			let exprValue, _, sideEffects, variables, expr = evaluate_expression variables isCertain context expr in
			if sideEffects <> [] then
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse ->
						defaultReturn, [], variables, AExpressionStatement(expr)
					| VBool true | VString _ | VNumber _ | VMemory _ | VUnknownButTrue -> 
						let return1, sideEffects1, variables, stmt = evaluate_statement labels variables isCertain context stmt in
						return1, sideEffects@sideEffects1, variables,(AIfStatement(AIf(expr,stmt)))
					| _ -> 
						let return1, sideEffects1, variables,stmt = evaluate_statement labels variables false context stmt in
						let variables = erase_sideEffects variables isCertain context sideEffects1 in
						let return1 = willMaybeReturn return1 in
						return1, sideEffects@sideEffects1, variables,(AIfStatement(AIf(expr,stmt))))
			else
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> defaultReturn, sideEffects, variables, AEmptyStatement
					| VBool true | VString _ | VNumber _ | VMemory _ | VUnknownButTrue ->
						evaluate_statement labels variables isCertain context stmt
					| _ -> 
						let return1, sideEffects1, _, stmt = evaluate_statement labels variables false context stmt in
						let variables = erase_sideEffects variables isCertain context sideEffects1 in
						let return1 = willMaybeReturn return1 in
						return1, sideEffects1, variables,(AIfStatement(AIf(expr,stmt))))

	and evaluate_iterationStatement labels variables isCertain context = function
		| ADoWhile(stmt,expr) -> let return1, sideEffects1, variables, stmt = evaluate_statement labels variables false context stmt in
			let exprValue, _, sideEffects,variables,expr = evaluate_expression variables false context expr in
			if sideEffects <> [] then
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
					return1, sideEffects1@sideEffects, variables,(AIterationStatement(ADoWhile(stmt,expr)))
					| _ -> 
					let variables = erase_sideEffects variables isCertain context sideEffects1 in
					return1, sideEffects1@sideEffects, variables,(AIterationStatement(ADoWhile(stmt,expr))))
			else
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
					let fullfill = fullfill_statement {
						idents = StrSet.empty;
						labels = labels;
						locals = StrSet.empty
						} stmt in
					let map,_,_ = Tools_identifier.free_ident (StrMap.empty,fullfill.labels, StrSet.empty) "?" false in
					let ident = StrMap.find "?" map in
					return1, sideEffects1@sideEffects, variables, ALabelledStatement (ident, Evaluator_labelize.labelize_statement ident stmt) 
					| _ -> 
					let variables = erase_sideEffects variables isCertain context sideEffects1 in
					return1, sideEffects1, variables,(AIterationStatement(ADoWhile(stmt,expr))))
		| AWhile(expr,stmt) ->
			let exprValue, _, sideEffects,variables,expr = evaluate_expression variables false context expr in
			if sideEffects <> [] then
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
						defaultReturn, sideEffects, variables,(AExpressionStatement(expr))
					| _ -> let return1, sideEffects1, variables,stmt = evaluate_statement labels variables false context stmt in
						let variables = erase_sideEffects variables isCertain context sideEffects1 in
						return1, sideEffects@sideEffects1, variables,(AIterationStatement(AWhile(expr,stmt))))
			else
				(match exprValue with
					| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> defaultReturn, [], variables, AEmptyStatement
					| _ -> let return1, sideEffects1, variables, stmt = evaluate_statement labels variables false context stmt in
						let variables = erase_sideEffects variables isCertain context sideEffects1 in
						return1, sideEffects1, variables,(AIterationStatement(AWhile(expr,stmt))))
		| AFor(expr1,expr2,expr3,stmt) ->
			(match expr1 with
				| None -> 
					(match expr2 with
						| None -> 
							(match expr3 with
								| None -> let return4, sideEffects4, variables, stmt = evaluate_statement labels variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects4 in
									return4, sideEffects4, variables,(AIterationStatement(AFor(expr1,expr2,expr3,stmt)))
								| Some expr3 -> let _,_,sideEffects3,variables,expr3 = evaluate_expression variables false context expr3 in
									let return4, sideEffects4, variables, stmt = evaluate_statement labels variables isCertain context stmt in
									let variables = erase_sideEffects variables isCertain context (sideEffects3@sideEffects4) in
									return4, sideEffects3@sideEffects4, variables,(AIterationStatement(AFor(expr1,expr2,Some(expr3),stmt))))
						| Some expr2 ->
							let exprValue, _, sideEffects,variables,expr2 = evaluate_expression variables false context expr2 in
							let sideEffects3,variables,expr3 = (match expr3 with
									| None -> [], variables, None
									| Some expr3 -> let _, _, sideEffects3,variables, expr3 = evaluate_expression variables false context expr3 in
										sideEffects3,variables,(Some expr3)) in
								if sideEffects <> [] || sideEffects3 <> [] then
									(match exprValue with
										| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
										defaultReturn, sideEffects, variables,(AExpressionStatement(expr2))
										| _ -> let return4, sideEffects4, variables, stmt = evaluate_statement labels variables false context stmt in
										let variables = erase_sideEffects variables isCertain context (sideEffects@sideEffects3@sideEffects4) in
									return4, sideEffects@sideEffects3@sideEffects4, variables,(AIterationStatement(AFor(expr1,Some(expr2),expr3,stmt))))
								else
									(match exprValue with
										| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
											defaultReturn, [], variables,AEmptyStatement
										| _ -> let return4, sideEffects4, variables, stmt = evaluate_statement labels variables false context stmt in
										let variables = erase_sideEffects variables isCertain context sideEffects4 in
									return4, sideEffects4, variables,(AIterationStatement(AFor(expr1,Some(expr2),expr3,stmt)))))
				| Some expr1 ->
					let _, _, sideEffects1,variables,expr1 = evaluate_expression variables isCertain context expr1 in
					(match expr2 with
					| None -> 
						(match expr3 with
							| None -> let return4, sideEffects4, variables, stmt = evaluate_statement labels variables false context stmt in
								let variables = erase_sideEffects variables isCertain context sideEffects4 in
								return4, sideEffects1@sideEffects4, variables,(AIterationStatement(AFor(Some(expr1),expr2,expr3,stmt)))
							| Some expr3 -> let _,_,sideEffects3,variables,expr3 = evaluate_expression variables false context expr3 in
							let return4, sideEffects4, variables, stmt = evaluate_statement labels variables false context stmt in
								let variables = erase_sideEffects variables isCertain context (sideEffects3@sideEffects4) in
								return4, sideEffects1@sideEffects3@sideEffects4, variables,(AIterationStatement(AFor(Some(expr1),expr2,Some(expr3),stmt))))
					| Some expr2 ->
						let exprValue,_,sideEffects,variables,expr2 = evaluate_expression variables false context expr2 in
							let sideEffects3,variables,expr3 = (match expr3 with
									| None -> [], variables, None
									| Some expr3 -> let _, _, sideEffects3,variables, expr3 = evaluate_expression variables false context expr3 in
										sideEffects3,variables,(Some expr3)) in
							if sideEffects <> [] || sideEffects3 <> [] then
								(match exprValue with
									| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse ->
									defaultReturn, sideEffects1@sideEffects, variables,(AExpressionStatement(if sideEffects1 <> [] then expr1@expr2 else expr2))
									| _ -> let return4, sideEffects4, variables, stmt = evaluate_statement labels variables false context stmt in
									let sideEffects = sideEffects@sideEffects3@sideEffects4 in
									let variables = erase_sideEffects variables isCertain context sideEffects in
								return4, sideEffects1@sideEffects, variables,(AIterationStatement(AFor(Some(expr1),Some(expr2),expr3,stmt))))
							else
								(match exprValue with
									| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse ->
									defaultReturn, sideEffects1, variables,(if sideEffects1 <> [] then (AExpressionStatement(expr1)) else AEmptyStatement)
									| _ -> let return4, sideEffects4, variables, stmt = evaluate_statement labels variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects4 in
								return4, sideEffects1@sideEffects4, variables,(AIterationStatement(AFor(Some(expr1),Some(expr2),expr3,stmt))))))
		| AForVar(is_meta, varDecl,expr2,expr3,stmt) ->
			let return1, sideEffects1, variables, (_,_,varDecl) = evaluate_variableStatement variables isCertain context (true,is_meta,varDecl) in
				(match expr2 with
				| None -> 
					(match expr3 with
						| None -> let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
							let variables = erase_sideEffects variables isCertain context sideEffects2 in
							return2, sideEffects1@sideEffects2, variables,(AIterationStatement(AForVar(is_meta,varDecl,expr2,expr3,stmt)))
						| Some expr3 -> let _,_,sideEffects3,variables,expr3 = evaluate_expression variables false context expr3 in
							let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
							let variables = erase_sideEffects variables isCertain context (sideEffects3@sideEffects2) in
							return2, sideEffects1@sideEffects3@sideEffects2, variables,(AIterationStatement(AForVar(is_meta,varDecl,expr2,Some(expr3),stmt))))
				| Some expr2 ->
					let exprValue,_,sideEffects,variables,expr2 = evaluate_expression variables false context expr2 in
						(if sideEffects <> [] then
							(match exprValue with
								| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
									defaultReturn, sideEffects, variables,(
									ABlock(
										AVariableStatement(true,is_meta,varDecl) :: 
											AExpressionStatement(expr2)::[]
									))
								| _ -> let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
								let variables = erase_sideEffects variables isCertain context (sideEffects@sideEffects2) in
							return2, sideEffects1@sideEffects@sideEffects2, variables,(AIterationStatement(AForVar(is_meta, varDecl,Some(expr2),expr3,stmt))))
						else
							(match exprValue with
								| VBool false | VString "" | VNumber 0. | VUndefined | VNull | VUnknownButFalse -> 
									defaultReturn, sideEffects1@sideEffects, variables,(AVariableStatement(true,is_meta,varDecl))
								| _ -> let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
							return2, sideEffects1@sideEffects@sideEffects2, variables,(AIterationStatement(AForVar(is_meta, varDecl,Some(expr2),expr3,stmt))))))
		| AForIn(lhs,expr,stmt) ->
			let _, refValue, sideEffects1, variables, lhs = evaluate_leftHandSideExpression variables isCertain context lhs in
			let exprValue, _, sideEffects2, variables, expr = evaluate_expression variables isCertain context expr in
				(match exprValue with
					| VBool _ | VString "" | VNumber _ | VUndefined | VNull | VUnknownButFalse -> 
						defaultReturn, sideEffects1@sideEffects2, variables, (if sideEffects1 <> [] then 
						(AExpressionStatement([
							ACondExpr(
								ACondBinaryExpr(
									AUnaryExpr(
										APostfixExpr(
											ALeftHandSide(lhs)
							))))]@(if sideEffects2 <> [] then expr else [])))
						else (if sideEffects2 <> [] then (AExpressionStatement(expr)) else AEmptyStatement))
					| VMemory(index) -> let exprValue = memory_get index in
						(match exprValue with
							| MArray(map,exact) ->
								(if exact && (map = StrMap.empty) then
								defaultReturn, sideEffects1@sideEffects2, variables, (if sideEffects1 <> [] then 
						(AExpressionStatement([
							ACondExpr(
								ACondBinaryExpr(
									AUnaryExpr(
										APostfixExpr(
											ALeftHandSide(lhs)
						))))]@(if sideEffects2 <> [] then expr else []))) else (if sideEffects2 <> [] then (AExpressionStatement(expr)) else AEmptyStatement))
									else let return3, sideEffects3, variables, stmt = evaluate_statement labels variables false context stmt in
									let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
									let variables = erase_sideEffects variables isCertain context sideEffects in
									return3, sideEffects, variables, (AIterationStatement(AForIn(lhs,expr,stmt))))
							| MFunction (_,_,_,map,exact) ->
								if exact && StrMap.is_empty map then
								defaultReturn, sideEffects1@sideEffects2, variables, (if sideEffects1 <> [] then 
						(AExpressionStatement([
							ACondExpr(
								ACondBinaryExpr(
									AUnaryExpr(
										APostfixExpr(
											ALeftHandSide(lhs)
						))))]@(if sideEffects2 <> [] then expr else []))) else (if sideEffects2 <> [] then (AExpressionStatement(expr)) else AEmptyStatement))
								else
								let return3, sideEffects3, variables, stmt = evaluate_statement labels variables false context stmt in
									let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
									let variables = erase_sideEffects variables isCertain context sideEffects in
									return3, sideEffects, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
							| MObject(items,items2,items3,exact) ->
								if exact && StrMap.is_empty items && StrMap.is_empty items2 && StrMap.is_empty items3 then
									defaultReturn, sideEffects1@sideEffects2, variables, (if sideEffects1 <> [] then 
						(AExpressionStatement([
							ACondExpr(
								ACondBinaryExpr(
									AUnaryExpr(
										APostfixExpr(
											ALeftHandSide(lhs)
						))))]@(if sideEffects2 <> [] then expr else []))) else (if sideEffects2 <> [] then (AExpressionStatement(expr)) else AEmptyStatement))
								else let return3, sideEffects3, variables, stmt = evaluate_statement labels variables false context stmt in
									let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
									let variables = erase_sideEffects variables isCertain context sideEffects in
									return3, sideEffects, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
							| _ -> let return3, sideEffects3, variables, stmt = evaluate_statement labels variables false context stmt in
								let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
								let variables = erase_sideEffects variables isCertain context sideEffects in
								return3, sideEffects, variables, (AIterationStatement(AForIn(lhs,expr,stmt)))
							)
					| _ -> let return3, sideEffects3, variables, stmt = evaluate_statement labels variables false context stmt in
						let sideEffects = sideEffects1@[(refValue,VUnknown)]@sideEffects2@sideEffects3 in
						let variables = erase_sideEffects variables isCertain context sideEffects in
						return3, sideEffects, variables, (AIterationStatement(AForIn(lhs,expr,stmt))))
		| AForVarIn(is_meta, varDecl, expr, stmt) -> 
			let return1, sideEffects1, variables, varDecl = evaluate_variableDeclaration true variables isCertain context varDecl in
			let exprValue, _, sideEffects, variables, expr = evaluate_expression variables isCertain context expr in
				(match exprValue with
					| VBool _  | VString "" | VNumber _ | VUndefined | VNull | VUnknownButFalse -> 
						return1, sideEffects1@sideEffects, variables, 
						(if sideEffects <> [] then (AExpressionStatement(expr)) else AEmptyStatement)
					| VMemory(index) -> let exprValue = memory_get index in
						(match exprValue with
							| MArray (map,exact) ->
								(if exact && (map = StrMap.empty) then 
									defaultReturn, sideEffects1@sideEffects, variables,
										(if sideEffects <> [] then (AExpressionStatement(expr)) else AEmptyStatement)
								else let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects2 in
									return2, sideEffects1@sideEffects@sideEffects2, variables, (AIterationStatement(AForVarIn(is_meta, varDecl,expr,stmt))))
							| MFunction (_,_,_,map,exact) ->
								if exact && StrMap.is_empty map then
									defaultReturn, sideEffects1@sideEffects, variables,
										(if sideEffects <> [] then (AExpressionStatement(expr)) else AEmptyStatement)
								else
								let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects2 in
									return2, sideEffects1@sideEffects@sideEffects2, variables, (AIterationStatement(AForVarIn(is_meta, varDecl,expr,stmt)))
							| MObject (items1,items2,items3,exact) ->
								if exact && StrMap.is_empty items1 && StrMap.is_empty items2 && StrMap.is_empty items3 then
									defaultReturn, sideEffects1@sideEffects, variables,
									(if sideEffects <> [] then (AExpressionStatement(expr)) else AEmptyStatement)
								else let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
									let variables = erase_sideEffects variables isCertain context sideEffects2 in
									return2, sideEffects1@sideEffects@sideEffects2, variables, (AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt)))
							| _ -> let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
								let variables = erase_sideEffects variables isCertain context sideEffects2 in
								return2, sideEffects1@sideEffects@sideEffects2, variables, (AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt))))
					| _ -> let return2, sideEffects2, variables, stmt = evaluate_statement labels variables false context stmt in
						let variables = erase_sideEffects variables isCertain context sideEffects2 in
						return2, sideEffects1@sideEffects@sideEffects2, variables, (AIterationStatement(AForVarIn(is_meta,varDecl,expr,stmt))))

	and evaluate_expression variables isCertain context = function
		| [] -> VBool(false), defaultRefValue, [], variables, []
		| [a] -> let exprValue, refValue, sideEffects, variables, a = evaluate_assignmentExpression variables isCertain context a in
			exprValue, refValue, sideEffects, variables, [a]
		| a::b -> let _, _, sideEffects1, variables, a = evaluate_assignmentExpression variables isCertain context a in
			let exprValue, refValue, sideEffects2, variables, b = evaluate_expression variables isCertain context b in
				exprValue, refValue, sideEffects1 @ sideEffects2, variables, a::b

	and evaluate_assignmentExpression variables isCertain context = function
		| ACondExpr(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_conditionalExpression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, ACondExpr(expr)
		| AAssignOp(lhs,op,expr) -> 
			let exprValue, refValue1, sideEffects1, variables, lhs = evaluate_leftHandSideExpression variables isCertain context lhs in
			let exprValue2, refValue2, sideEffects2, variables, expr = evaluate_assignmentExpression variables isCertain context expr in
			let context = (match refValue1.ref_index with
				| Some index -> 
					{
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| None -> context) in
				
			let sideEffects, variables = set_value variables isCertain context exprValue2 refValue1.ref_value in
			exprValue2, refValue2, sideEffects1 @ sideEffects2 @ sideEffects @ [(refValue1,exprValue2)], variables, AAssignOp(lhs,op,expr)

	and evaluate_leftHandSideExpression variables isCertain context = function
		| ANewExpression(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_newExpression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, ANewExpression(expr)
		| ACallExpression(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_callExpression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, expr

	and evaluate_newExpression variables isCertain context = function
		| AMemberExpression(expr) -> let exprValue, refValue, _, sideEffects, variables, expr = evaluate_memberExpression variables isCertain true context expr in
			exprValue, refValue, sideEffects, variables, AMemberExpression(expr)
		| ANewMemberExpression(expr) -> let exprValue, _, sideEffects1, variables, expr = evaluate_newExpression variables isCertain context expr in
			let newIndex = memory_push (MObject((StrMap.add "constructor" exprValue StrMap.empty),StrMap.empty,StrMap.empty,true)) in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (_,params,body,_,_) -> 
							let context = {
								co_context = context.co_context;
								co_current = Some newIndex;
								co_nesting = context.co_nesting
							} in
							let _, sideEffects = evaluate_realFunctionContent variables isCertain context params body [] in
							VMemory(newIndex), defaultRefValue, sideEffects1@sideEffects, variables, ANewMemberExpression(expr)
						| _ -> VMemory(newIndex), defaultRefValue, sideEffects1@[(defaultRefValue,VUnknown)], variables, ANewMemberExpression(expr)
					)
				| _ -> VMemory(newIndex), defaultRefValue, sideEffects1@[(defaultRefValue,VUnknown)], variables, ANewMemberExpression(expr)
			)

	
	and evaluate_callExpression variables isCertain context = function
		| ACallMemberArgs(
			AMemberDotExpr(expr, "call"), args ) -> let exprValue, refValue, context, sideEffects1, variables, expr 
			= evaluate_memberExpression variables isCertain true context expr in
			let values, sideEffects2, variables, args = evaluate_arguments variables isCertain context args in
			let defaultAst = ACallExpression(ACallMemberArgs(AMemberDotExpr (expr, "call"),args)) in
			let sideEffects = sideEffects1@sideEffects2@[defaultRefValue,VUnknown] in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (_,params,body,_,_) -> 
							(match values with
								| [] -> let variables = erase_sideEffects variables isCertain context sideEffects in
										VUnknown, defaultRefValue, sideEffects, variables, defaultAst
								| a::values -> 
									(match a with
										| VMemory index2 -> 
											let context = {
												co_context = index2;
												co_current = Some index2;
												co_nesting = context.co_nesting
											} in
											let value, sideEffects3 = evaluate_realFunctionContent variables isCertain context params body values in
											if sideEffects3 = [] then
												(match value with
													| VNumber n -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
													AMemberExpression(APrimaryExpr(ALiteral(ALitNumericLiteral((string_of_flint n))))))
													| VBool b -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
													AMemberExpression(APrimaryExpr(ALiteral(ABooleanLiteral(b)))))
													| _ -> value, defaultRefValue, sideEffects1@sideEffects2, variables, defaultAst
												)
											else
											value, defaultRefValue, sideEffects1@sideEffects2@sideEffects3, variables, defaultAst
										| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
											VUnknown, defaultRefValue, sideEffects, variables, defaultAst
									)
							)
						| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
								VUnknown, defaultRefValue, sideEffects, variables, defaultAst
					)
				| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
					VUnknown, defaultRefValue, sideEffects, variables, defaultAst
			)
		
		| ACallMemberArgs(
			AMemberDotExpr(expr, "apply"), args ) -> let exprValue, refValue, context, sideEffects1, variables, expr 
			= evaluate_memberExpression variables isCertain true context expr in
			let values, sideEffects2, variables, args = evaluate_arguments variables isCertain context args in
			let defaultAst = ACallExpression(ACallMemberArgs(AMemberDotExpr (expr, "apply"),args)) in
			let sideEffects = sideEffects1@sideEffects2@[defaultRefValue,VUnknown] in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (_,params,body,_,_) -> 
							(match values with
								| a::lst ->
									let values = (match lst with
										| [] -> Some []
										| b::_ -> (match b with
											| (VMemory index3) -> 
												(match memory_get index3 with
													| MArray (values,_) -> 
														Some (jsArray_to_list values)
													| _ -> None
												)
											| _ -> None
										)) in
									(match a, values with
										| (VMemory index2), (Some values) ->
											
													let context = {
														co_context = index2;
														co_current = Some index2;
														co_nesting = context.co_nesting
													} in
													let value, sideEffects3 = evaluate_realFunctionContent variables isCertain context params body values in
													if sideEffects3 = [] then
														(match value with
															| VNumber n -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
															AMemberExpression(APrimaryExpr(ALiteral(ALitNumericLiteral((string_of_flint n))))))
															| VBool b -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
															AMemberExpression(APrimaryExpr(ALiteral(ABooleanLiteral(b)))))
															| _ -> value, defaultRefValue, sideEffects1@sideEffects2, variables, defaultAst
														)
													else
													value, defaultRefValue, sideEffects1@sideEffects2@sideEffects3, variables, defaultAst
											
										| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
											VUnknown, defaultRefValue, sideEffects, variables, defaultAst
									)
								| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
										VUnknown, defaultRefValue, sideEffects, variables, defaultAst
							)
						| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
								VUnknown, defaultRefValue, sideEffects, variables, defaultAst
					)
				| _ -> let variables = erase_sideEffects variables isCertain context sideEffects in
					VUnknown, defaultRefValue, sideEffects, variables, defaultAst
			)
	
		| ACallMemberArgs(expr,args) -> let exprValue, refValue, context, sideEffects1, variables, expr 
			= evaluate_memberExpression variables isCertain true context expr in
			let values, sideEffects2, variables, args = evaluate_arguments variables isCertain context args in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (_,params,body,_,_) -> 
							let value, sideEffects = evaluate_realFunctionContent variables isCertain context params body values in
							if sideEffects = [] then
								(match value with
									| VNumber n -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
									AMemberExpression(APrimaryExpr(ALiteral(ALitNumericLiteral((string_of_flint n))))))
									| VBool b -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
									AMemberExpression(APrimaryExpr(ALiteral(ABooleanLiteral(b)))))
									| _ -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ACallExpression(ACallMemberArgs(expr,args))
								)
							else
							value, defaultRefValue, sideEffects1@sideEffects2@sideEffects, variables, ACallExpression(ACallMemberArgs(expr,args))
						| _ -> let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
							let variables = erase_sideEffects variables isCertain context sideEffects in
							VUnknown, defaultRefValue, sideEffects, variables, ACallExpression(ACallMemberArgs(expr,args))
					)
				| _ -> 
					let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
					let variables = erase_sideEffects variables isCertain context sideEffects in
					VUnknown, defaultRefValue, sideEffects, variables, ACallExpression(ACallMemberArgs(expr,args))
			)
		| ACallExpressionArgs(expr, args) -> let exprValue, refValue, sideEffects1, variables, expr = evaluate_callExpression variables isCertain context expr in
			let expr = match expr with
				| ACallExpression expr -> expr
				| ANewExpression _ -> (raise (Evaluation_error) ) in
			let values, sideEffects2, variables, args = evaluate_arguments variables isCertain context args in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction(_,params,body,_,_) -> let value, sideEffects = evaluate_realFunctionContent variables isCertain context params body values in
							if sideEffects = [] then
								(match value with
									| VNumber n -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
									AMemberExpression(APrimaryExpr(ALiteral(ALitNumericLiteral((string_of_flint n))))))
									| VBool b -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ANewExpression(
									AMemberExpression(APrimaryExpr(ALiteral(ABooleanLiteral(b)))))
									| _ -> value, defaultRefValue, sideEffects1@sideEffects2, variables, ACallExpression(ACallExpressionArgs(expr,args))
								)
							else
							value, defaultRefValue, sideEffects1@sideEffects2@sideEffects, variables, ACallExpression(ACallExpressionArgs(expr,args))
						| _ -> let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
							let variables = erase_sideEffects variables isCertain context sideEffects in
							VUnknown, defaultRefValue, sideEffects, variables, ACallExpression(ACallExpressionArgs(expr,args))
					)
				| _ -> let sideEffects = sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)] in
					let variables = erase_sideEffects variables isCertain context sideEffects in
					VUnknown, defaultRefValue, sideEffects, variables, ACallExpression(ACallExpressionArgs(expr,args))
			)
		| ACallExpressionExpr(expr1, expr2) -> let exprValue1, refValue1, sideEffects1, variables, expr1 = evaluate_callExpression variables isCertain context expr1 in
			let expr1 = match expr1 with
				| ACallExpression expr -> expr
				| ANewExpression _ -> (raise (Evaluation_error) ) in
			let exprValue2, _, sideEffects2, variables, expr2 = evaluate_expression variables isCertain context expr2 in
			(*let refValue = refValue1@[exprValue2] in*)
			let refValue = {
				ref_value = exprValue2;
				ref_index = (match exprValue1 with
					| VMemory index -> Some index;
					| _ -> None )
			} in
			let context = (match exprValue1 with
				| VMemory index -> {
					co_context = index;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context) in
				
			let exprValue, sideEffects3, variables = 
				(match exprValue1 with
					| VMemory index -> get_value variables isCertain context exprValue2
					| _ -> VUnknown, [], variables) in
			exprValue, refValue, sideEffects1@sideEffects2@sideEffects3, variables, ACallExpression(ACallExpressionExpr(expr1,expr2))
		| ACallExpressionIdent(expr,ident) ->  let exprValue, refValue, sideEffects, variables, expr = evaluate_callExpression variables isCertain context expr in
			let expr = match expr with
				| ACallExpression expr -> expr
				| ANewExpression _ -> (raise (Evaluation_error) ) in
			let refValue = {
				ref_value = VString ident;
				ref_index = (match exprValue with
					| VMemory index -> Some index;
					| _ -> None )
			} in
			let context = (match exprValue with
				| VMemory index -> {
					co_context = index;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context) in
			
			let exprValue, sideEffects1, variables = 
				(match exprValue with
					| VMemory index -> get_value variables isCertain context (VString ident)
					| _ -> VUnknown, [], variables) in

			exprValue, refValue, sideEffects@sideEffects1, variables, ACallExpression(ACallExpressionIdent(expr,ident))

	and evaluate_conditionalExpression variables isCertain context = function
		| ACondBinaryExpr(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_binaryExpression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, ACondBinaryExpr(expr)
		| ACondTernaryExpr(expr1,expr2,expr3) ->
			let testValue, _, sideEffects1, variables, expr1 = evaluate_binaryExpression variables isCertain context expr1 in
			match testValue with
				| VBool false | VString "" | VNumber 0. ->
					let exprValue, refValue, sideEffects3, variables, expr3 = evaluate_assignmentExpression variables isCertain context expr3 in
					if sideEffects1 <> [] then
					exprValue, refValue, sideEffects1@sideEffects3, variables, 
						ACondBinaryExpr(
							AUnaryExpr(
								APostfixExpr(
									ALeftHandSide(
										ANewExpression(
											AMemberExpression(
												APrimaryExpr(
													AExpression(
														(ACondExpr(ACondBinaryExpr(expr1)))::[expr3]))))))))
					else
						exprValue, refValue, sideEffects3, variables, 
						ACondBinaryExpr(
							AUnaryExpr(
								APostfixExpr(
									ALeftHandSide(
										ANewExpression(
											AMemberExpression(
												APrimaryExpr(
													AExpression([expr3]))))))))
				| VBool true | VString _ | VNumber _ | VMemory _ -> 
					let exprValue, refValue, sideEffects2, variables, expr2 = evaluate_assignmentExpression variables isCertain context expr2 in
					if sideEffects1 <> [] then
						exprValue, refValue, sideEffects1@sideEffects2, variables, 
						ACondBinaryExpr(
							AUnaryExpr(
								APostfixExpr(
									ALeftHandSide(
										ANewExpression(
											AMemberExpression(
												APrimaryExpr(
													AExpression(
														(ACondExpr(ACondBinaryExpr(expr1)))::[expr2]))))))))
					else
						exprValue, refValue, sideEffects2, variables, 
						ACondBinaryExpr(
							AUnaryExpr(
								APostfixExpr(
									ALeftHandSide(
										ANewExpression(
											AMemberExpression(
												APrimaryExpr(
													AExpression([expr2]))))))))
				| _ -> 
				let exprValue2, _, sideEffects2, variables, expr2 = evaluate_assignmentExpression variables false context expr2 in
				let exprValue3, _, sideEffects3, variables, expr3 = evaluate_assignmentExpression variables false context expr3 in
				let variables = erase_sideEffects variables isCertain context (sideEffects2@sideEffects3) in
				(merge_types exprValue2 exprValue3), defaultRefValue, sideEffects1@sideEffects2@sideEffects3, variables, ACondTernaryExpr(expr1,expr2,expr3)
	
	and evaluate_memberExpression variables isCertain isNode context = function
		| APrimaryExpr(expr) -> let exprValue, refValue, sideEffects, variables, expr 
			= evaluate_primaryExpression variables isCertain context expr in
			exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects, variables, APrimaryExpr(expr)
		| AFunctionExpr(expr) -> let exprValue, refValue, sideEffects, variables, expr 
			= evaluate_functionExpression variables isCertain context expr in
			exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects, variables, AFunctionExpr(expr)
		| AMemberBracketExpr(expr1,expr2) -> let exprValue1, refValue1, context, sideEffects1, variables, expr1 
			= evaluate_memberExpression variables isCertain false context expr1 in
			let exprValue2, refValue2, sideEffects2, variables, expr2 = evaluate_expression variables isCertain context expr2 in
			let refValue = {
				ref_value = exprValue2;
				ref_index = (match exprValue1 with
					| VMemory index -> Some index
					| _ -> None)
			} in
			
			let exprValue, sideEffects3, variables = 
				(match exprValue1 with
					| VMemory index -> get_value variables isCertain context exprValue2
					| _ -> VUnknown, [], variables) in

			exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects1@sideEffects2@sideEffects3, variables, AMemberBracketExpr(expr1,expr2)
		| AMemberDotExpr(expr,ident) -> let exprValue, refValue, context, sideEffects, variables, expr 
			= evaluate_memberExpression variables isCertain false context expr in

			
			let refValue = {
				ref_value = VString ident;
				ref_index = (match exprValue with
					| VMemory index -> Some index
					| _ -> None)
			} in
			
			let exprValue, sideEffects1, variables = 
				(match exprValue with
					| VMemory index -> get_value variables isCertain context (VString ident)
					| _ -> VUnknown, [], variables) in
			
			exprValue, refValue, (if isNode then context else match exprValue with
				| VMemory index -> {
					co_context = context.co_context;
					co_current = Some index;
					co_nesting = context.co_nesting
					}
				| _ -> context), sideEffects@sideEffects1, variables, AMemberDotExpr(expr,ident)
		| AMemberNewExpr(expr,args) -> let exprValue, _, context, sideEffects1, variables, expr = evaluate_memberExpression variables isCertain false context expr in
			let values, sideEffects2, variables, args = evaluate_arguments variables isCertain context args in
			let newIndex = memory_push (MObject((StrMap.add "constructor" exprValue StrMap.empty),StrMap.empty,StrMap.empty,true)) in
			(match exprValue with
				| VMemory index -> 
					(match memory_get index with
						| MFunction (_,params,body,_,_) -> 
							let context = {
								co_context = context.co_context;
								co_current = Some newIndex;
								co_nesting = context.co_nesting
							} in
							let _, sideEffects = evaluate_realFunctionContent variables isCertain context params body values in
							VMemory(newIndex), defaultRefValue, context, sideEffects1@sideEffects2@sideEffects, variables, AMemberNewExpr(expr,args)
						| _ -> VMemory(newIndex), defaultRefValue, context, sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)], variables, AMemberNewExpr(expr,args)
					)
				| _ -> VMemory(newIndex), defaultRefValue, context, sideEffects1@sideEffects2@[(defaultRefValue,VUnknown)], variables, AMemberNewExpr(expr,args)
			)

	and evaluate_arguments variables isCertain context = function
		| [] -> [], [], variables, []
		| expr::args -> let value, _, sideEffects1, variables, expr = evaluate_assignmentExpression variables isCertain context expr in
			let values, sideEffects2, variables, args = evaluate_arguments variables isCertain context args in
			value::values, sideEffects1@sideEffects2, variables, expr::args

	and  evaluate_binaryExpression variables isCertain context = function
		| AUnaryExpr(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_unaryExpression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, AUnaryExpr(expr)
		| ABinaryExpr(op,expr1,expr2) -> let exprValue1, refValue, sideEffects1, variables, expr1 = evaluate_binaryExpression variables isCertain context expr1 in
			let exprValue, refValue, sideEffects2, variables, expr = (match op with
			| "||" -> (match exprValue1 with
				| VString "" | VBool false | VNumber 0. | VUnknownButFalse | VNull | VUndefined -> 
					let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
					exprValue2, refValue, sideEffects2, variables, (if sideEffects1 = [] then expr2 else  ABinaryExpr(op, expr1, expr2))
				| VString _ | VBool true | VNumber _ | VUnknownButTrue | VMemory _ -> exprValue1, refValue, [], variables, expr1
				| VUnknown -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables false context expr2 in
					(*(match exprValue2 with
						| VString "" | VBool false | VNumber 0. | VUnknownButFalse | VNull | VUndefined -> 
							VUnknown, refValue, sideEffects2, variables, (if sideEffects2 = [] then expr1 else ABinaryExpr(op, expr1, expr2))
						| VString _ | VBool true | VNumber _ | VUnknownButTrue | VMemory _ -> 
							VUnknown, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
						| VUnknown -> VUnknown, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2) 
					)*)
					VUnknown, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
				)
			| "&&" -> (match exprValue1 with
				| VString "" | VBool false | VNumber 0. | VUnknownButFalse | VNull | VUndefined -> 
					exprValue1, refValue, [], variables, expr1
				| VString _ | VBool true | VNumber _ | VUnknownButTrue | VMemory _ -> 
					let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
					exprValue2, refValue, sideEffects2, variables, (if sideEffects1 = [] then expr2 else  ABinaryExpr(op, expr1, expr2))
				| VUnknown -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables false context expr2 in
					(*(match exprValue2 with
						| VString "" | VBool false | VNumber 0. | VUnknownButFalse | VNull | VUndefined -> 
							VUnknown, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
						| VString _ | VBool true | VNumber _ | VUnknownButTrue | VMemory _ -> 
							VUnknown, refValue, sideEffects2, variables, (if sideEffects2 = [] then expr1 else ABinaryExpr(op, expr1, expr2))
						| VUnknown -> VUnknown, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
					)*)
					VUnknown, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
				)
			| "==" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VString s1 -> (match exprValue2 with
							| VString s2 -> VBool (s1 = s2)
							| VBool _ | VNumber _ | VUndefined | VNull -> VBool false
							| _ -> VUnknown
						)
					| VBool b1 -> (match exprValue2 with
						| VBool b2 -> VBool (b1 = b2)
						| VNumber 0. -> VBool (b1 = false)
						| VNumber 1. -> VBool b1
						| VString _ | VNumber _ | VNull -> VBool false
						| _ -> VUnknown )
					| VMemory index1 -> (match exprValue2 with
						| VMemory index2 -> VBool (index1 = index2)
						| VBool _ | VNumber _ | VNull | VUndefined | VString _ -> VBool false
						| _ -> VUnknown )
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 = i2)
						| VBool true -> VBool (i1 = 1.)
						| VBool false -> VBool (i1 = 0.)
						| VString _ | VNull | VUndefined -> VBool false
						| VUnknownButFalse -> if i1 = 1. then VBool false else VUnknown
						| VUnknownButTrue -> if i1 = 0. then VBool false else VUnknown
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| "!=" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VString s1 -> (match exprValue2 with
							| VString s2 -> VBool (s1 <> s2)
							| VBool _ | VNumber _ | VUndefined | VNull -> VBool true
							| _ -> VUnknown
						)
					| VBool b1 -> (match exprValue2 with
						| VBool b2 -> VBool (b1 <> b2)
						| VNumber 0. -> VBool b1
						| VNumber 1. -> VBool (b1 = false)
						| VString _ | VNumber _ | VNull -> VBool true
						| _ -> VUnknown )
					| VMemory index1 -> (match exprValue2 with
						| VMemory index2 -> VBool (index1 <> index2)
						| VBool _ | VNumber _ | VNull | VUndefined | VUnknownButFalse | VString _ -> VBool true
						| _ -> VUnknown )
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 <> i2)
						| VBool true -> VBool (i1 <> 1.)
						| VBool false -> VBool (i1 <> 0.)
						| VString _ | VNull | VUndefined -> VBool true
						| VUnknownButFalse -> if i1 = 1. then VBool true else VUnknown
						| VUnknownButTrue -> if i1 = 0. then VBool true else VUnknown
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| "+" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 +. i2)
						| VString i2 -> VString ((string_of_flint i1) ^ i2)
						| VBool i2 -> VNumber (i1 +. (if i2 then 1. else 0.))
						| _ -> VUnknown )
					| VString i1 -> (match exprValue2 with
						| VString i2 -> VString (i1 ^ i2)
						| VNumber i2 -> VString (i1 ^ (string_of_flint i2))
						| VBool i2 -> VString (i1 ^ (if i2 then "true" else "false"))
						| _ -> VUnknown)
					| VBool i1 -> (match exprValue2 with
						| VString i2 -> VString ((if i1 then "true" else "false") ^ i2)
						| VNumber i2 -> VNumber ((if i1 then 1. else 0.) +. i2)
						| VBool i2 -> VNumber ((if i1 then 1. else 0.) +. (if i2 then 1. else 0.))
						| _ -> VUnknown
						)
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| "-" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 -. i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| "*" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 *. i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| "/" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VNumber (i1 /. i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| "<" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 < i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| ">" -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
				let exprValue = (match exprValue1 with
					| VNumber i1 -> (match exprValue2 with
						| VNumber i2 -> VBool (i1 > i2)
						| _ -> VUnknown )
					| _ -> VUnknown
				) in
				exprValue, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			| _ -> let exprValue2, refValue, sideEffects2, variables, expr2 = evaluate_binaryExpression variables isCertain context expr2 in
					VUnknown, refValue, sideEffects2, variables, ABinaryExpr(op, expr1, expr2)
			) in
			exprValue, refValue, sideEffects1@sideEffects2, variables, (match exprValue with
				| VUnknownButTrue | VUnknownButFalse | VUnknown | VMemory _ -> expr
				| _ -> unaryExpression_from_jsValue exprValue )

	and evaluate_primaryExpression variables isCertain context = function
		| AThis -> VMemory(context.co_context), {
				ref_value = VString "this";
				ref_index = None
			}, [], variables, AThis
		| AIdentifier(ident) -> 
			let exprValue, _, variables = get_value variables isCertain context (VString ident) in
			exprValue, {
				ref_value = VString(ident);
				ref_index = None
			}, [], variables, AIdentifier(ident)
		| ALiteral(literal) -> let exprValue, refValue, sideEffects, variables, literal = evaluate_literal variables isCertain context literal in
			exprValue, refValue, sideEffects, variables, ALiteral(literal)
		| AArrayLiteral(literal) -> let exprValue, refValue, sideEffects, variables, literal = evaluate_arrayLiteral variables isCertain context literal in
			exprValue, refValue, sideEffects, variables, AArrayLiteral(literal)
		| AObjectLiteral(literal) -> let exprValue, refValue, sideEffects, variables, literal = evaluate_objectLiteral variables isCertain context literal in
			exprValue, refValue, sideEffects, variables, AObjectLiteral(literal)
		| AExpression(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_expression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, AExpression(expr)

	and evaluate_unaryExpression variables isCertain context = function
		| APostfixExpr(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_postfixExpression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, APostfixExpr(expr)
		| AOpUnaryExpr(op, expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_unaryExpression variables isCertain context expr in
			let exprValue, sideEffects2, variables = match op with
				| "--" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n-.1.)) refValue.ref_value in 
						(VNumber (n-.1.)), sideEffects@[(refValue, VNumber (n-.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						VUnknown, sideEffects@[(refValue, VUnknown)], variables 
					)
				| "++" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n+.1.)) refValue.ref_value in 
						(VNumber (n+.1.)), sideEffects@[(refValue, VNumber (n+.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						VUnknown, sideEffects@[(refValue, VUnknown)], variables )
				| "!" -> (match exprValue with
					| VBool b -> VBool (not b)
					| VUnknownButTrue -> VBool false
					| VUnknownButFalse -> VBool true
					| VNumber 0. -> VBool true
					| VNumber _ -> VBool false
					| _ -> VUnknown ), [], variables
				| "+" -> (match exprValue with
					| VNumber n -> VNumber n
					| VBool true -> VNumber 1.
					| VBool false -> VNumber 0.
					| _ -> VUnknown ), [], variables
				| "-" -> (match exprValue with
					| VNumber n -> VNumber (-. n)
					| VBool true -> VNumber (-1.)
					| VBool false -> VNumber 0.
					| VString _ -> VUnknownButFalse
					| _ -> VUnknown ), [], variables
				| "~" -> (match exprValue with
					| VNumber 0. -> VNumber (-1.)
					| VNumber _ -> VUnknownButTrue 
					| VBool true -> VNumber (-2.)
					| VBool false -> VNumber (-1.)
					| VString _ -> VNumber (-1.)
					| VMemory _ -> VNumber (-1.)
					| _ -> VUnknown ), [], variables
				| "void" -> VUndefined, [], variables
				| "delete" -> (match exprValue with
					| VNumber _ -> (VBool true), [], variables
					| VString _ -> (VBool true), [], variables
					| VBool _ -> (VBool true), [], variables
					| VMemory _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in 
							VUnknown, sideEffects@[refValue,VUnknown], variables
					| _ -> VUnknown, sideEffects@[refValue,VUnknown], variables)
				| "typeof" -> (match exprValue with
					| VNumber _ -> VString "number"
					| VString _ -> VString "string"
					| VMemory index -> ((match memory_get index with
						| MFunction _ -> VString "function"
						| MUnknown -> VUnknownButTrue
						| _ -> VString "object" ))
					| _ -> VUnknownButTrue), [], variables
				| _ -> VUnknown, [], variables in
				exprValue, refValue, sideEffects@sideEffects2, variables, AOpUnaryExpr(op, expr)
	
	and evaluate_literal variables isCertain context = function
		| ANull -> VNull, defaultRefValue, [], variables, ANull
		| ABooleanLiteral(value) -> VBool(value), defaultRefValue, [], variables, ABooleanLiteral(value)
		| ALitNumericLiteral(literal) -> VNumber(float_of_string literal), defaultRefValue, [], variables, ALitNumericLiteral(literal)
		| ALitStringLiteral(literal) -> VString(literal), defaultRefValue, [], variables, ALitStringLiteral(literal)
		| ARegularExpressionLiteral(regularExpression) -> let index = memory_push (MObject(StrMap.empty,StrMap.empty,StrMap.empty,false)) in
			VMemory(index), defaultRefValue, [], variables, ARegularExpressionLiteral(regularExpression)
			
	and evaluate_arrayLiteral variables isCertain context = function
		| AElisions(elisions) -> let lst = List.fold_left (fun lst _ -> lst@[VUndefined]) [] elisions in
			let index = create_array lst in
			VMemory(index), defaultRefValue, [], variables, AElisions(elisions)
		| AElements(elements) ->  let lst, sideEffects, variables, elements = 
			List.fold_left (fun (lst, sideEffects, variables, elements) e -> 
				let lst1, sideEffects1, variables, element = evaluate_element variables isCertain context e in
				lst@lst1, sideEffects@sideEffects1, variables, elements@[element]) ([], [], variables, []) elements in
			let index = create_array lst in
			VMemory(index), defaultRefValue, sideEffects, variables, AElements(elements)
		| AElementsAndElisions(elements, elisions) ->
			let lst, sideEffects, variables, elements = 
			List.fold_left (fun (lst, sideEffects, variables, elements) element -> 
				let lst1, sideEffects1, variables, element = evaluate_element variables isCertain context element in
				lst@lst1, sideEffects@sideEffects1, variables, elements@[element]) ([], [], variables, []) elements in
			let lst = List.fold_left (fun lst  _ -> lst@[VUndefined]) lst elisions in
			let index = create_array lst in
			VMemory(index), defaultRefValue, sideEffects, variables, AElementsAndElisions(elements,elisions)

	and evaluate_element variables isCertain context (elisions, expr) =
		let lst = List.fold_left (fun lst  _ -> lst@[VUndefined]) [] elisions in
		let exprValue, refValue, sideEffects, variables, expr = evaluate_assignmentExpression variables isCertain context expr in
		lst@[exprValue], sideEffects, variables, (elisions,expr)
		
	and evaluate_objectLiteral variables isCertain context exprs =
		let both,getter,setter, sideEffects, variables, exprs = List.fold_left (fun (both, getter, setter, sideEffects, variables, exprs) expr ->
			let both, getter, setter, sideEffects1, variables, expr = evaluate_propertyAssignment both getter setter variables isCertain context expr in
			both, getter, setter, sideEffects@sideEffects1, variables, exprs@[expr]) (StrMap.empty,StrMap.empty,StrMap.empty, [],variables,[]) exprs in
		let index = create_object both getter setter in
		VMemory(index), defaultRefValue, sideEffects, variables, exprs

	and evaluate_propertyAssignment both getter setter variables isCertain context = function
		| APropAssignment(name,expr) -> let key = evaluate_propertyName name in
			let exprValue, _, sideEffects, variables, expr = evaluate_assignmentExpression variables isCertain context expr in
			(StrMap.add key exprValue both), getter, setter, sideEffects, variables, APropAssignment(name,expr)
		| APropGet(name,body) -> let key = evaluate_propertyName name in
			let _,sideEffects,body = evaluate_functionContent variables isCertain context [] body in
			(*let index = create_function [] body true in*)
			both, (StrMap.add key (false,[],body) getter), setter, sideEffects, variables, APropGet(name,body)
		| APropSet(name,params,body) -> let key = evaluate_propertyName name in
			let _,sideEffects,body = evaluate_functionContent variables isCertain context params body in
			(*let index = create_function params body true in*)
			both, getter, (StrMap.add key (false,params,body) setter), sideEffects, variables, APropSet(name,params,body)
	
	and evaluate_propertyName = function
		| AIdentifierName(str) -> str
		| AStringLiteral(str) -> str
		| ANumericLiteral(str) -> string_of_flint (float_of_string str)
	
	and evaluate_postfixExpression variables isCertain context = function
		| ALeftHandSide(expr) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_leftHandSideExpression variables isCertain context expr in
			exprValue, refValue, sideEffects, variables, ALeftHandSide(expr)
		| ALeftHandSideOp(expr,op) -> let exprValue, refValue, sideEffects, variables, expr = evaluate_leftHandSideExpression variables isCertain context expr in
			let sideEffects2, variables = match op with
			| "--" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n-.1.)) refValue.ref_value in 
						sideEffects@[(refValue, VNumber (n-.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						sideEffects@[(refValue, VUnknown)], variables 
					)
			| "++" -> (match exprValue with
					| VNumber n -> let sideEffects, variables = set_value variables isCertain context (VNumber (n+.1.)) refValue.ref_value in 
						sideEffects@[(refValue, VNumber (n+.1.))], variables
					| _ -> let sideEffects, variables = set_value variables isCertain context VUnknown refValue.ref_value in
						sideEffects@[(refValue, VUnknown)], variables 
					)
			| _ -> [], variables in
		exprValue, refValue, sideEffects@sideEffects2, variables, ALeftHandSideOp(expr,op)

	in

	evaluate_program ast
